import sys, os, socket
sys.path.insert(0, os.getcwdu().split(os.sep+"GMT"+os.sep)[0])

import SocketServer, BaseHTTPServer
import logging
import httplib
from threading import Thread
from PatchedQueue import PatchedQueue
from GMT.miner.miner import Miner
from GMT.messaging.xmlpickle import pickle, unpickle
from GMT.messaging import messages

APPENGINE_HOST = "7.latest.mygmail-trends.appspot.com"
POST_REQUEST = "/post"

def doPost(xml):
    webservice = httplib.HTTP(APPENGINE_HOST)
    webservice.putrequest("POST", POST_REQUEST)
    webservice.putheader("Host", APPENGINE_HOST)
    webservice.putheader("User-Agent", "Python post")
    webservice.putheader("Content-type", "application/xml; charset=\"UTF-8\"")
    webservice.putheader("Content-length", "%d" % len(xml))
    webservice.endheaders()
    webservice.send(xml)
     
#    statuscode, statusmessage, header = webservice.getreply()
#    print "Response: ", statuscode, statusmessage
#    print "headers: ", header
#    res = webservice.getfile().read()
#    print res

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    
    def __init__(self, request, client_address, server):
        self._miner = server.miner
        BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, client_address, server)
        

    def do_POST(self):
        try:
            # get request
            if self.headers.has_key("content-length") and int(self.headers["content-length"]) > 0:
                xml_in = self.rfile.read(int(self.headers["content-length"]))
            else:
                raise Exception("invalid request: content-length is either missing or not positive.")
            #print "xml_in: \n%s" % xml_in          
            request_list = unpickle(xml_in)
            sync_req_list = []
            async_req_list = []
            if not isinstance(request_list, list):
                request_list = [request_list]
            for req in request_list:
                if req.async:
                    async_req_list.append(req)
                else:
                    sync_req_list.append(req)
            
            # if we got sync request first handle it and only then insert the async requests into the queue
            if (len(sync_req_list) > 0):
                request = sync_req_list[0]
                response = messages.Response(request.id, "OK")
                self.process_sync_request(request, response)
                for request in async_req_list:
                    server.queue.put(request)
            # if we got only async requests put them in the queue one by one
            elif (len(async_req_list) > 0):
                response = messages.Response(None, "OK")
                for request in async_req_list:
                    server.queue.put(request)
                response.data = "Requests have been queued successfully"
            # invalid input
            else:
                response = messages.Response(None, "INPUT_ERROR")
            
            xml_out = pickle(response)
            
            # got a valid XML RPC response
            #logging.info(xml_out)
            self.write_response(200, xml_out)
        except Exception, exp:
            # internal error, report as HTTP server error
            if (exp.errno == 10053): 
                logging.info("connection with host has been lost.")
                return
            self.write_response(500, str(exp.args))
            
            
    def write_response(self, code, content):
        self.send_response(code)
        self.send_header("Content-type", "text/xml")
        self.send_header("Content-length", str(len(content)))
        self.end_headers()
        self.wfile.write(content)
        self.wfile.flush()
        self.connection.shutdown(1)
        
    def process_sync_request(self, request, response):
        logging.info("process_sync_request is handling %s request for user: %s" % (request.time_span, request.username))
        response.data = self._miner.mine(request.username, 
                                         request.password,
                                         request.get_timespan_date())
        response.time_span = request.time_span

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    """Handle requests in a separate thread."""
    
    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, num_worker_threads=5):
        # init miner
        self.miner = Miner()
        # create a queue and worker threads to fetch its messages
        self.queue = PatchedQueue()
        for i in range(num_worker_threads):
            worker = Thread(target=self.QueueReader, args=(self.queue, self.miner))
            worker.setDaemon(True)
            worker.start()
        
        # init the TCP server    
        SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
    
#    def process_request(self, request, client_address):
#        """ overriding request processing by simple putting the request in queue. """
#        self.queue.put((request, client_address))
    
    def QueueReader(self, q, miner):
        """ Reads a queue item in a worker thread and handle it. """
        while True:
            request = q.get()
            logging.info("QueueReader has read %s request for user: %s" % (request.time_span, request.username))
            response = messages.Response(request.id, "OK")
            response.time_span = request.time_span
            
            try:
                response.data = miner.mine(request.username, 
                                           request.password, 
                                           request.get_timespan_date())
                if (len(response.data) == 0):
                    del response
                    q.task_done()
                    return
                
                try:
                    xml_out = pickle(response)
                    doPost(xml_out)
                except Exception, exp:
                    logging.info("Async sending results failed with exception %s\n%s" % (str(type(exp)), str(exp.args)))
            except Exception, exp:
                logging.info("Async mining raised an exception %s\nargs=%s\n%s" % (str(type(exp)), str(exp.args), str(exp)))
                
            q.task_done()
            
if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG, format="[%(asctime)s] %(message)s")
    
    port = 40404
    server = ThreadedTCPServer(('', port), RequestHandler)
    print "server started on port %d\n" % port
    server.serve_forever()
    
